supce's blog

CSS Secret 读书笔记之沿环形路径转动的动画


沿环形路径转动的动画

今天利用CSS实现一种沿着环形路径转动的动画。为了便于演示,我们让一个头像图片沿着环形路径转动。
首先是一个img标签,用来保存头像图片,外面嵌套一个div用来设置背景。

<div class="path">
    <img src="test.jpg" class="avatar">
</div>

然后设置基本的样式:

.path{
    width: 200px;
    height: 200px;
    margin:20px auto;
    padding: 20px;
    border-radius: 50%;
    background: #fb3;
}
.avatar,{
    width: 40px;
    height: 40px;
    margin: 0 auto;
    display: block;
    border-radius: 50%;
    border: 1px dashed black; /*为了便于观察旋转,添加虚线边框*/
    overflow: hidden;
}
.avatar-a>img{
    display: block;
    width: inherit;
}

最后为图片添加旋转动画:

@keyframes spin{
    to { transform: rotate(1turn); }
}
.avatar{
    animation: 6s spin infinite linear;
    transform-origin: 50% 100px;
}

刷新页面:

虽然图片已经能够沿着环形路径转动了,但是图片自身也在旋转。为了让图片能够沿着环形转动,同时保证图片保持原来的朝向,可以用下面这两种方式:

既然图片本身也在旋转,那可以尝试在图片的外层嵌套一个div,然后让这个div反向旋转,从而把内层的图片旋转效果给抵消掉。这也是之前经常用到的一种方法。

修改HTML代码结构:

<div class="path">
    <div class="avatar-a">
        <img src="test.jpg">
    </div>
</div>

下面就利用到上篇文章提到的animation-direction,图片外层的div保持旋转,然后设置内部img的animation-direction为相同的动画特效,但是最后让animation-direction反转。

@keyframes spin{
    to { transform: rotate(1turn); }
}
.avatar-a{
    animation: 6s spin infinite linear;
    transform-origin: 50% 100px;
}
.avatar-a>img{
    animation: inherit;
    animation-direction: reverse;
}

much nicer!

问题虽然得到解决,但是修改了HTML结构。下面这种方式能够在保持原来HTML代码结构不变的情况下解决自身旋转问题。在提出这种方法之前,要首先了解下transform-origin。其实:

每个transform-origin都可以被两个translate()模拟出来

比如下面这个两端代码是等效的:

transform: rotate(30deg);
transform-origin: 100px 50px;

transform: translate(100px,50px),
           rotate(30deg),
           translate(-100px,-50px);
transform-origin: 0 0;

具体过程如下图:

于是,我们可以把刚才的spin动画拆分:

@keyframe spin{
    from{
        transform: translate(50%,100px)
                   rotate(0turn)
                   translate(-50%,-100px);
    }
    to{
        transform: translate(50%,100px)
                   rotate(1turn)
                   translate(-50%,-100px);
    }
}

对于自身的反向旋转进行拆分:

@keyframe spin{
    from{
        transform: translate(50%,50%)
                   rotate(1turn)
                   translate(-50%,-50%);
    }
    to{
        transform: translate(50%,50%)
                   rotate(0turn)
                   translate(-50%,-50%);
    }
}

然后把img外部嵌套的div去掉,把这两个动画合并,直接用在img上,这样既保持了原有的HTML代码结构,也解决了图片的自旋转问题。最终代码如下:

HTML:

<div class="path">
    <img src="test.jpg" class="avatar-b">
</div>

CSS:

@keyframes spin-b{
    from{
        transform: translate(50%,100px)
                   rotate(0turn)
                   translate(-50%,-100px)
                   translate(50%,50%)
                   rotate(1turn)
                   translate(-50%,-50%);
    }
    to{
        transform: translate(50%,100px)
                   rotate(1turn)
                   translate(-50%,-100px)
                   translate(50%,50%)
                   rotate(0turn)
                   translate(-50%,-50%);
    }
}
.avatar-b{
    animation: 6s spin-b infinite linear;
}

也可以把一个关键帧的水平位移抵消,以精简代码:

@keyframes spin-b{
    from{
        transform: translateY(100px) translateY(-50%)
                   rotate(0turn)
                   translateY(-100px) translateY(50%)
                   rotate(1turn);

    }
    to{
        transform: translateY(100px) translateY(-50%)
                   rotate(1turn)
                   translateY(-100px) translateY(50%)
                   rotate(0turn);
    }
}

much nicer!